/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package client;
import java.io.*;
import java.net.Socket;
import java.util.Hashtable;
import java.util.LinkedList;

/**
 *
 * @author juan
 */
public class PI {
    public PI(String host,int port, String user, String pwd, String connType, ConnectionPanel gui) {
        this.host = host;
        this.port = port;
        this.user = user;
        this.pwd = pwd;
        this.gui = gui;
        if(connType.equals("Active"))
            this.connType = ACTIVE;
        else
            this.connType = PASSIVE;
        this.id = counter++;
    }
    //----------Command and response functions-----
    public void sendCommand(String msg, Object... args) {
        out.printf(msg+"\r\n", args);
        //System.out.printf(">>>>"+msg+"\r\n", args);
        gui.protocolLog.append(">>"+this.id+">>"+String.format(msg, args)+"\n");
    }

    public Response getResponse() {
        try {
            Response r = new Response(in.readLine());
            //System.out.println("<<<"+r);
            gui.protocolLog.append("<<"+this.id+"<<"+r+"\n");
            return r;
        } catch (Exception e) {return null;}
    }

    //--------------helper functions------------
    public String getWorkingDir() {
        return gui.getWorkingDir();
    }

    public String getRemoteWorkingDir() {
        return remoteWorkingDirectory;
    }
    //-------------Connection----------------

    public boolean connect() {
        try {
            socket = new Socket(host,port);
            out = new PrintStream(socket.getOutputStream());
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            return true;
        } catch(Exception e) {
            return false;
        }
    }

    public boolean login() {
        try {
            //Get servers salute
            Response r = getResponse();
            socket.setSoTimeout(500);
            while(r==null)
                r = getResponse();
            while(r!=null) {
                r = getResponse();
            }
            socket.setSoTimeout(0);
            //Send username and password
            sendCommand("USER %s",user);
            r = getResponse();
            switch(r.code) {
                case 230:
                    return true;
                case 331:
                    sendCommand("PASS %s",pwd);
                    r = getResponse();
                    return r.code == 230;

                default:
                    return false;
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    
    public void logout() {
        sendCommand("QUIT");
    }

    /**
     * Sends the command PWD through the contro conn and return
     * the string of the response;
     * @return the current (remote) working directory;
     */
    public String requestPWD() {
        sendCommand("PWD");
        Response r = getResponse();
        int s = r.msg.indexOf('"')+1;
        int e = r.msg.lastIndexOf('"');
        String rwd = r.msg.substring(s, e);
        if(rwd.equals("/"))
            remoteWorkingDirectory = "";
        else
            remoteWorkingDirectory = rwd;
        return rwd;
    }

    public Response requestMakeDir(String dir) {
        dir = getRemoteWorkingDir() + '/' +dir;
        sendCommand("MKD %s",dir);
        return getResponse();
    }

    public Response requestRemoveDir(String dir) {
        dir = getRemoteWorkingDir() + '/' +dir;
        sendCommand("RMD %s",dir);
        return getResponse();
    }

    public Response requestCWD(String dir) {
        sendCommand("CWD %s",dir);
        return getResponse();
    }

    public Response requestDeleteFile(String fileName) {
        String path = getRemoteWorkingDir() + '/' + fileName;
        sendCommand("DELE %s", path);
        return getResponse();
    }

    public Response requestRenameFile(String oldName, String newName) {
        sendCommand("RNFR %s",oldName);
        Response r = getResponse();
        if(r.code == 350) {
            sendCommand("RNTO %s",newName);
            return getResponse();
        }
        return r;
    }

    //---------Commands that use DTP
    public LinkedList requestFileList() {
        sendCommand("TYPE A");
        getResponse();
        DTP dtp = configureDTP(DTP.LIST,"-la");
        dtp.connect();
        sendCommand("LIST");
        fileDataDic = new Hashtable<String, RemoteFile>();
        LinkedList<RemoteFile> ret = null;
        while(true) {
            Response r = getResponse();
            //125 150
            switch(r.code) {
                case 125:
                case 150:
                    //ready or almost ready to transmit
                    ret = dtp.recieveList();
                    for(int i=0; i<ret.size(); i++)
                        fileDataDic.put(ret.get(i).toString(), ret.get(i));
                    break;
                case 226:
                case 250:
                    //transfer ok close connection
                    dtp.closeConnection();
                    return ret;
                default:
                    // TODO talves reintentar la conneccion aki
                    dtp.closeConnection();
                    return null;
            }
        }
    }

    public void retrieveFile(String fileName) {
        sendCommand("TYPE I");
        getResponse();
        DTP dtp = configureDTP(DTP.RETR, getWorkingDir()+"/"+fileName);
        dtp.connect();
        sendCommand("RETR %s",getRemoteWorkingDir()+"/"+fileName);

        while(true) {
            Response r = getResponse();
            switch(r.code) {
                case 125:
                case 150:
                    //ready or almost ready to transmit
                    new Thread(dtp).start();
                    break;
                case 226:
                case 250:
                    //transfer ok close connection
                    dtp.closeConnection();
                    return;
                default:
                    // TODO talves reintentar la conneccion aki
                    dtp.closeConnection();
                    return;
            }
        }
    }

    public void storeFile(String fileName) {
        sendCommand("TYPE I");
        getResponse();
        DTP dtp = configureDTP(DTP.STOR,getWorkingDir()+"/"+fileName);
        dtp.connect();
        sendCommand("STOR %s",getRemoteWorkingDir() + '/' + fileName);
        
        while(true) {
            Response r = getResponse();
            switch(r.code) {
                case 125:
                case 150:
                    //ready or almost ready to transmit
                    new Thread(dtp).start();
                    break;
                case 226:
                case 250:
                    //transfer ok close connection
                    dtp.closeConnection();
                    requestPWD();
                    requestFileList();
                    return;
                default:
                    // TODO talves reintentar la conneccion aki
                    dtp.closeConnection();
                    requestPWD();
                    requestFileList();
                    return;
            }
        }
    }
    //-----------------
    
    public DTP configureDTP(int operation,String opArg) {
        DTP dtp = null;
        switch(connType) {
            case ACTIVE:
                try {
                    String h = socket.getLocalAddress().toString().substring(1);
                    dtp = new DTP(h,0,DTP.ACTIVE,this,operation,opArg);
                } catch(Exception e) {
                    // TODO notify the user about error.
                    System.out.println(e);
                    return null;
                }
                break;
            case PASSIVE:

                try {
                    sendCommand("PASV");
                    Response r = getResponse();
                    if(r.code == 227) {
                        //(%s,%s,%s)
                        int i = r.msg.indexOf("(");
                        int j = r.msg.indexOf(")");
                        String data[] = r.msg.substring(i+1,j).split(",");
                        String host = data[0] + "." + data[1] + "." + data[2] + "." + data[3];
                        int port = (Integer.parseInt(data[4])<<8) + Integer.parseInt(data[5]);
                        //System.out.println(host+","+port);
                        dtp = new DTP(host, port, DTP.PASSIVE, this,operation,opArg);
                    } else {}
                }catch(Exception e) {
                    // TODO notify the user about error.
                    System.out.println(e);
                    return null;
                }
                break;
        }
        return dtp;
    }


    Socket socket;
    PrintStream out;
    BufferedReader in;
    //PI configuration variables
    String host, user, pwd;
    int port, connType;
    //Constants
    public static final int ACTIVE = 0, PASSIVE = 1;
    //Connection status
    boolean connected = false;
    //DTP dtp = null;
    Hashtable<String, RemoteFile> fileDataDic = new Hashtable<String, RemoteFile>();
    ConnectionPanel gui;
    String remoteWorkingDirectory = "";
    public Object[] remoteWDFiles = null;
    int id;
    static int counter = 0;
}
